using System;
using System.IO;
using System.Globalization;
using System.Collections.Generic;
using System.Text;
using Heaven.Mathematics;

namespace Heaven.Geometry.Importers
{
    /// <summary>
    /// This is a parser of ASCII Stereolith. Files 
    /// which holds this data have *.stl extension.
    /// (full support for binary and ASCII content)
    /// </summary>
    public class Stereolith
    {
        #region Fields

        // Name of the object
        string name = null;
        // Meshe
        Polygonal mesh = null;

        #endregion

        #region Properties

        /// <summary>
        /// Name of the object
        /// </summary>
        public string Name
        {
            get { return name; }
        }

        /// <summary>
        /// Meshe
        /// </summary>
        public Polygonal Mesh
        {
            get { return mesh; }
        }

        #endregion

        #region Parsing

        /// <summary>
        /// Parses the given stream
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static Stereolith Parse(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);
            
            if(Encoding.ASCII.GetString(reader.ReadBytes(300)).Contains("facet normal "))
            {
                stream.Seek(0, SeekOrigin.Begin);
                return ParseAscii(stream);
            }
            else
            {
                stream.Seek(0, SeekOrigin.Begin);
                return ParseBinary(stream);
            }
        }

        static Stereolith ParseAscii(Stream stream)
        {
            StreamReader reader = new StreamReader(stream, Encoding.ASCII);
            List<UnifiedVertex> positions = new List<UnifiedVertex>();

            while(!reader.EndOfStream)
            {
                string line = reader.ReadLine();
   
                // Starting new object
                if(line.StartsWith("endsolid"))
                {
                    Polygonal polygonal = new Polygonal();
                    polygonal.AddVertices(positions.ToArray());
                    for (int i = 0; i < positions.Count; i += 3)
                    {
                        polygonal.AddTriangle(i, i + 2, i + 1);
                    }
                        
                    Stereolith result = new Stereolith();
                    result.name = line.Substring(8);
                    result.mesh = polygonal;
                    return result;
                }

                // Searching vertices
                string[] parts = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                if(parts.Length == 0) continue;
                
                if(parts[0] == "vertex")
                {
                    positions.Add(new UnifiedVertex(new Vector3(
                        Double.Parse(parts[1], CultureInfo.InvariantCulture),
                        Double.Parse(parts[2], CultureInfo.InvariantCulture),
                        Double.Parse(parts[3], CultureInfo.InvariantCulture))
                        ));
                }
            }

            throw new ArgumentException("The given stream with STL data is empty");
        }

        static Stereolith ParseBinary(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);
            // Any creator's descrition 80 bytes
            string descriprion = Encoding.ASCII.GetString(reader.ReadBytes(80));
            // Reading int equal to the number of facets in file
            UnifiedVertex[] positions = new UnifiedVertex[reader.ReadInt32() * 3];

            
            for(int i = 0; i < positions.Length; i+=3)
            {
                // Skiping a normal
                reader.BaseStream.Seek(12, SeekOrigin.Current);
                // Loading a vertices
                positions[i] = new UnifiedVertex(new Vector3(
                    reader.ReadSingle(),
                    reader.ReadSingle(),
                    reader.ReadSingle()
                    ));
                positions[i + 1] = new UnifiedVertex(new Vector3(
                    reader.ReadSingle(),
                    reader.ReadSingle(),
                    reader.ReadSingle()
                    ));
                positions[i + 2] = new UnifiedVertex(new Vector3(
                    reader.ReadSingle(),
                    reader.ReadSingle(),
                    reader.ReadSingle()
                    ));
                // Skiping padding
                reader.BaseStream.Seek(2, SeekOrigin.Current);
            }

            Polygonal polygonal = new Polygonal();
            polygonal.AddVertices(positions);
            for (int i = 0; i < positions.Length; i += 3)
            {
                polygonal.AddTriangle(i, i + 2, i + 1);
            }
            
            Stereolith result = new Stereolith();
            result.name = descriprion;
            result.mesh = polygonal;
            return result;
        }

        #endregion
    }
}
